#include <iostream>
#include <stdlib.h>
#include <cstring>
#include <signal.h>
#include <sys/wait.h>
#include <unistd.h>
#include <unistd.h>
using namespace std;

void PrintBit(sigset_t signos)
{
    int signo = 1;
    while (signo <= 32)
    {
        if (sigismember(&signos, signo))
            cout << "1"
                 << " ";
        else
            cout << "0"
                 << " ";
        signo++;
    }
    cout << endl;
}

void handler(int sig)
{
    // 查看pending表是何时更改的，是捕获前还是捕获后。
    // 如果打出来为0，则说明捕获前更改了。如果没有则说明是捕获后更改的 -- os层次上
    sigset_t pending;
    int cnt = 1;
    while (cnt)
    {
        cout << "Handler Pending:";
        sigpending(&pending);
        PrintBit(pending);
        if (cnt == 15)
            break;
        cnt++;
        sleep(1);
    }

    cout << getpid() << "："
         << "我捕获到了信号" << sig << endl;
}

void test_signal()
{
    // 捕获2号信号~
    signal(SIGINT, handler);
    while (true)
    {
        cout << getpid() << " :我还没有收到信号呢" << endl;
        sleep(1);
    }
}
void test_kill()
{
    for (int signo = 1; signo < 32; signo++)
        signal(signo, handler);
    int cnt = 0;
    while (cnt < 32)
    {
        cout << "--------" << endl;
        if (cnt == 9)
            cnt++;
        kill(getpid(), cnt);
        cout << "--------" << endl;

        cnt++;
        sleep(1);
    }
    kill(getpid(), 9);
}
void test_raise()
{
    signal(2, handler);
    cout << "--------------" << endl;
    sleep(1);
    cout << "--------------" << endl;
    raise(2);
    cout << "--------------" << endl;
}
void test_abort()
{
    signal(6, handler);
    cout << "--------------" << endl;
    sleep(1);
    cout << "--------------" << endl;
    abort();
    cout << "--------------" << endl;
}

void handler_alarm(int signo)
{
    cout << " signo = " << signo << endl;
    int n = alarm(0);
    cout << "剩余时间：" << n << endl;
}

void test_alarm()
{
    signal(SIGALRM, handler_alarm);
    int cnt = 0;
    while (true)
    {
        if (cnt == 0)
            alarm(10);
        cout << getpid() << " " << cnt << " : ------------------" << endl;
        cnt++;
        sleep(1);
    }
    alarm(10);
}

void test_core_dump()
{
    // ulimit -c 设置核心转储文件大小
    // int* pa = nullptr;
    // *pa = 11;

    int pid = fork();
    if (pid == 0) // 子进程
    {
        while (true)
        {
            cout << "我是子进程" << getpid() << " 我正在模拟异常行为的CORE处理" << endl;
            sleep(1);
        }
        exit(1);
    }
    int status = 0;
    waitpid(pid, &status, 0);
    if (WIFEXITED(status)) // 返回真，正常退出
    {
        cout << "子进程异常正常退出, exit code = " << WEXITSTATUS(status) << endl;
    }
    else // 异常退出，收到信号
    {
        // 0111 1111
        int core_dump = (status >> 7) & 1;
        int signo = status & 0x7F;
        cout << "子进程异常退出, signo = " << signo << " core dump = " << core_dump << endl;
    }
    // 虚拟机共享目录下生成不了核心转储文件
    // 生成的核心转储文件是用来对当前执行程序进行调试用的
    // gdb mytest
    // (gdb) core-file core.1523(核心转储文件)
    // 快速定位到出错的地方
}

void test_sigset()
{
    // 对2号信号做屏蔽，并返回老的屏蔽字，并将其比特位打印
    sigset_t nset, oset;
    // 1.清空信号集
    sigemptyset(&nset);
    sigemptyset(&oset);
    // 2.设置 将2号信号添加到信号集
    sigaddset(&nset, SIGINT);
    // 3.将新信号集设置进 信号屏蔽字中
    sigprocmask(SIG_BLOCK, &nset, &oset);
    sigset_t pending;
    int cnt = 0;
    cout << "OBlock:";
    PrintBit(oset);

    // 对二号信号进行捕获
    // signal(2,handler);
    struct sigaction nact;
    struct sigaction oact;
    memset(&nact, 0, sizeof(nact));
    memset(&oact, 0, sizeof(oact));
    // 设置捕获方法
    nact.sa_handler = handler;
    nact.sa_flags = 0;
    // 屏蔽其他信号,是在对2号信号进行捕获时，屏蔽其他信号
    sigaddset(&nact.sa_mask, 3);
    sigaddset(&nact.sa_mask, 4);
    sigaddset(&nact.sa_mask, 5);
    sigaction(2, &nact, &oact);

    while (true)
    {
        cout << "Pending:";
        sigpending(&pending);
        PrintBit(pending);
        cout << getpid() << " - 我在测试中 " << endl;
        if (cnt == 9)
        {
            // 4.解除对2号信号的阻塞
            sigprocmask(SIG_SETMASK, &oset, &nset);
            // 5.重新捕获2号信号
            // signal(2,SIG_DFL);
            sigaction(2, &oact, &nact);

            cout << "Block: ";
            PrintBit(nset);
        }
        sleep(1);
        cnt++;
    }
}


volatile int quit = 0;
void handler2(int signo)
{
    quit = 1;
    cout << "对 " << signo << " 进行捕获" << endl;
}
void test_volatile()
{
    // 当进行O2 级别的优化后，程序不能退出了。
    // 原因是优化后，os第一次执行while时,把quit=0 load到cpu寄存器中了
    // 以后os读取quit从cpu特定寄存器中读取，而不是内存
    // 信号捕获中quit是写入内存的
    // 解决使用volatile解决内存不可见的问题
    signal(2,handler2);
    while(!quit);
    cout << "正常退出" << endl;
}


int main()
{
    test_volatile();
    return 0;
}